<template>
    <div class="t-gen-form">
        <el-row :gutter="formConfCopy.gutter">
            <el-form :ref="formConfCopy.formRef"
                     :disabled="formConf.disabled"
                     :label-position="formConfCopy.labelPosition"
                     :label-width="formConfCopy.labelWidth + 'px'"
                     :model="formModel"
                     :rules="formRules"
                     :size="formConfCopy.size"
            >
                <template v-for="(item,index) in formConfCopy.fields">
                    <generate-form-item
                        :key="item.key"
                        :cid="item.vModel"
                        :index="index"
                        :item="item"
                        :models.sync="formModel"
                        :rules="formRules"
                        :seq-no="getSeqNo(index,item)"
                        :style="isFieldShow(item)"
                        @next="nextPage"
                        @prev="prevPage"
                        @input-change="onInputChange"
                        @ocr-success="handleOcrSuccess"
                    />
                </template>
                <el-col v-if="formConfCopy.formBtns">
                    <el-form-item class="submit-btn-form-item" size="large" style="margin-top:30px">
                        <el-button
                            type="primary"
                            @click="submitForm"
                        >
                            {{ formConfCopy.submitBtnText ? formConfCopy.submitBtnText : '提交' }}
                        </el-button>
                        <el-button v-if="formConfCopy.resetBtn" @click="resetForm">
                            重置
                        </el-button>
                    </el-form-item>
                </el-col>
            </el-form>
        </el-row>
    </div>
</template>

<script>

import {handleChangeLabel} from './FormItemLabel'
import {deepClone} from '../../utils/index'
import _ from 'lodash'
import '../FormItem/autoRegister.js'
import {evalExpression} from '../../utils/expression'
import {execRemoteAPI} from '../../api/execRemoteAPI'
import GenerateFormItem from './GenerateFormItem'
import {throttle} from 'throttle-debounce'
import FormValidateRule from './FormValidateRule'
import {saveFormData} from '../../utils/db'

let seqNo = 0

export default {
    name: 'GenerateForm',
    components: {
        GenerateFormItem
    },
    mixins: [FormValidateRule],
    props: {
        formConf: {
            type: Object,
            required: true
        },
        // 被触发了逻辑显示的字段列表
        logicTriggerItemList: {
            type: Array,
            required: false
        },
        pageFormModel: {
            type: Object,
            required: false
        }
    },
    data() {
        return {
            // 维护一个对象存储 字段名和字段的index 避免查找
            fieldIdIndex: {},
            formConfCopy: deepClone(this.formConf),
            formModel: deepClone(this.pageFormModel),
            formRules: {}

        }
    },
    computed: {},
    created() {
        // 初始化动态数据 比如SELECT等远程数据
        this.initDynamicOptions(this.formConfCopy.fields)
        // 初始化默认值
        this.initFormData(this.formConfCopy.fields, this.formModel)
        // 构建表单校验规则
        this.buildRules(this.formConfCopy.fields, this.formRules)
        // 特殊处理分页
        seqNo = this.formConfCopy.startSeqNo || 0

    },
    mounted() {
    },
    methods: {
        getKey() {
            return new Date()
        },
        // 初始化表单动态选项
        async initDynamicOptions(componentList) {
            for (const cur of componentList) {
                const config = cur.config
                // 数据为从接口获取
                if (config.optionsType && config.optionsType === 1) {
                    execRemoteAPI.get(config.dynamicOptions.url)
                        .then(res => {
                            const options = _.get(res.data, config.dynamicOptions.dataPath)
                            if (options && _.isArray(options)) {
                                const dyOptions = options.map(op => ({
                                    value: _.get(op, config.dynamicOptions.valueField),
                                    label: _.get(op, config.dynamicOptions.labelField)
                                }))
                                console.log(dyOptions)
                                config.options = dyOptions
                            }
                        })
                }
                // 从字典获取
                if (config.optionsType && config.optionsType === 2) {
                    let dictRes = await execRemoteAPI.get(`/system/dict/data/type/${config.dictOptionType}`)
                    config.options = dictRes.data.data.map(op => ({
                        value: op.dictValue,
                        label: op.dictLabel
                    }))
                }
            }
        },
        // 初始化表单默认值
        initFormData(componentList, formData) {
            // 设置默认值
            componentList.forEach((cur, index) => {
                const config = cur.config
                this.fieldIdIndex[cur.vModel] = index
                // 如果存在分页带回的数据 则不再设置默认 0是checkbx radio等的其他输入框
                if (cur.vModel && !formData[cur.vModel] && 0 != formData[cur.vModel]) {
                    formData[cur.vModel] = config.defaultValue
                    handleChangeLabel(formData, cur, config.defaultValue)
                }
                if (config.children) this.initFormData(config.children, formData)
            })
        },
        // 表单校验规则
        buildRules(componentList, rules) {
            componentList.forEach(cur => {
                // 逻辑不显示必填问题不校验
                const triggerShow = _.indexOf(this.logicTriggerItemList, cur.formItemId) > -1
                // cur.logicShow 是否逻辑显示字段  triggerShow 已经被触发显示的问题
                // 是逻辑显示字段  并且还没满足条件被显示·
                if (cur.logicShowField !== true || triggerShow) {
                    this.buildRule(cur, rules)
                }
            })
        },
        removeRule(cur) {
            delete this.formRules[cur.vModel]
        },
        resetForm() {
            this.formConfCopy = deepClone(this.formConf)
            this.$refs[this.formConf.formRef].resetFields()
        },
        submitForm: throttle(3000, true, function() {
            this.$refs[this.formConf.formRef].validate(valid => {
                if (!valid) {
                    // 未选中自动高亮
                    // if (document.getElementsByClassName('el-FormItem-item__error').length > 0) {
                    //
                    // }
                    setTimeout(() => {
                        const isError = document.getElementsByClassName('is-error')
                        isError[0].querySelector('input')
                            .focus()
                    }, 100)
                    return false
                }
                // 触发submit事件
                console.log(JSON.stringify(this.formModel))
                this.$emit('submit', {formModel: this.formModel})
                return true
            })
        }),
        /**
         *  表单发生值变化
         * @param value 值
         * @param field 字段名
         * @param item  发生改变表单组件对象
         */
        onInputChange(value, field, item) {
            this.$emit('on-change', field, value, this.models)
            this.logicShowHandle(value, field, item)
            // 临时存储数据到本地 下次进入可以选择回显
            this.tempSaveFormData()
        },
        /**
         * 分页下一页
         */
        nextPage(page) {
            this.switchPage('next', page)
        },

        /**
         * 分页上一页
         */
        prevPage(page) {
            this.switchPage('prev', page)
        },
        /**
         * 切换页
         */
        switchPage(eventName, page) {
            this.$refs[this.formConf.formRef].validate(valid => {
                if (!valid) {
                    setTimeout(() => {
                        const isError = document.getElementsByClassName('is-error')
                        isError[0].querySelector('input')
                            .focus()
                    }, 100)
                    return false
                }
                this.$emit(eventName, {
                    page,
                    startSeqNo: this.formConfCopy.startSeqNo,
                    endSeqNo: seqNo,
                    formModel: this.formModel
                })
                return true
            })
        },
        // 字段是否能显示
        isFieldShow(item) {
            // 不是逻辑显示字段
            if (!item.logicShowField) {
                return ''
            }
            // 表单被重新渲染 控制逻辑显示隐藏
            const triggerShow = _.indexOf(this.logicTriggerItemList, item.vModel) > -1
            //  或者已经被触发显示了
            return triggerShow ? '' : 'display:none'
        },
        getSeqNo(index, item) {
            // 是否开启显示标题
            if (!this.formConfCopy.theme || this.formConfCopy.theme.showNumber !== true) {
                return null
            }
            // 处于显示状态并且组件不是辅助显示类型
            if (!this.isFieldShow(item) && item.displayType == false) {
                seqNo++
            }
            return seqNo
        },
        // 数据变更时 处理逻辑显示相关数据
        logicShowHandle(value, field, item) {
            seqNo = this.formConfCopy.startSeqNo || 0
            // 值被包装过 需要取出原始值
            const {logicShowRule} = this.formConfCopy
            if (!logicShowRule) {
                return
            }
            // 找到该问题需要触发显示的问题 判断逻辑是否成立
            const rules = _.get(logicShowRule, field)
            if (rules && Array.isArray(rules)) {
                rules.forEach(r => {
                    // 成立让该对应的问题显示出来
                    const flag = evalExpression(this.formModel, r.logicExpression)
                    if (flag) {
                        // 显示加上值必填等校验
                        let triggerFormItem = this.formConfCopy.fields[this.fieldIdIndex[r.triggerFormItemId]]
                        // 重新渲染组件 不然必填红点不显示
                        if (triggerFormItem) {
                            triggerFormItem.key = new Date().getTime()
                            this.buildRule(triggerFormItem, this.formRules)
                            // 设置默认值
                            this.$set(this.formModel, r.triggerFormItemId, triggerFormItem.config.defaultValue)
                        }
                        // 防止表单重新渲染 display被刷新
                        this.logicTriggerItemList.push(r.triggerFormItemId)
                        let triggerEle = document.querySelector(`div[cid="${r.triggerFormItemId}"]`)
                        if (triggerEle) {
                            triggerEle.style.display = ''
                        }
                    } else {
                        _.remove(this.logicTriggerItemList, n => n === r.triggerFormItemId)
                        // 移除表单的必填校验
                        let triggerFormItem = this.formConfCopy.fields[this.fieldIdIndex[r.triggerFormItemId]]
                        if (triggerFormItem) {
                            this.removeRule(triggerFormItem)
                        }
                        // 移除已经填写的值
                        delete this.formModel[r.triggerFormItemId]
                        let triggerEle = document.querySelector(`div[cid="${r.triggerFormItemId}"]`)
                        if (triggerEle) {
                            triggerEle.style.display = 'none'
                        }
                    }
                })
            }
        },
        /**
         * OCR识别完成之后 把数据填充到表单内
         * @param val
         */
        handleOcrSuccess(val) {
            Object.keys(val).forEach(key => {
                this.$set(this.formModel, key, val[key])
                // 更改组件key 让组件重新渲染 不然数据不会显示到表单
                let item = this.formConfCopy.fields[this.fieldIdIndex[key]]
                item.key = new Date() + key
            })
        },
        /**
         * 临时存储到localstore
         */
        tempSaveFormData: throttle(430, true, function() {
            // 是否打开临时存储选项
            if (this.formConf.setting && this.formConf.setting.saveNotSubmitStatus) {
                saveFormData(this.formModel, this.formConf.formKey)
            }
        })
    }
}
</script>

<style lang="scss" scoped>
.submit-btn-form-item {
  text-align: center;
}

</style>
